package com.gl.glbooks.services;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import javax.naming.directory.Attributes;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.flex.messaging.MessageTemplate;
import org.springframework.ldap.NamingException;
import org.springframework.ldap.core.AttributesMapper;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.ldap.filter.AndFilter;
import org.springframework.ldap.filter.EqualsFilter;
import org.springframework.transaction.annotation.Transactional;

import com.gl.glbooks.repository.GenericRepository;
import com.gl.glbooks.spring.aop.aspectj.cache.Descachea;
import com.gl.glbooks.vos.Categoria;
import com.gl.glbooks.vos.Comentario;
import com.gl.glbooks.vos.Confirmacion;
import com.gl.glbooks.vos.Constants;
import com.gl.glbooks.vos.Domicilio;
import com.gl.glbooks.vos.Ejemplar;
import com.gl.glbooks.vos.EstadoEjemplar;
import com.gl.glbooks.vos.EstadoPrestamo;
import com.gl.glbooks.vos.EstadoSugerenciaPDFsubido;
import com.gl.glbooks.vos.Libro;
import com.gl.glbooks.vos.LibroDigital;
import com.gl.glbooks.vos.LibroFisico;
import com.gl.glbooks.vos.Oficina;
import com.gl.glbooks.vos.Prestamo;
import com.gl.glbooks.vos.Sugerencia;
import com.gl.glbooks.vos.SugerenciaPDFsubido;
import com.gl.glbooks.vos.Usuario;

import flex.messaging.FlexContext;


public class LibroServices implements ILibroServices {

	protected final Log logger = LogFactory.getLog(getClass());

	@SuppressWarnings("rawtypes")
	@Autowired
	GenericRepository repo;

	@Autowired
	IListarServices listarService;
	
	@Autowired
	IAgregarServices agregarService;
	
	@Autowired
	private IMailServices mailServices;
	
	@Autowired
	MessageTemplate defaultMessageTemplate;

	@Autowired
	LdapTemplate ldapTemplate;
	
	@Override
	@Transactional
	@Descachea(cacheKey = "libros")
	public Confirmacion devolverLibro(Prestamo prestamo,Boolean estadoDevolucion) {
		Prestamo prestamoBusqueda;
		Ejemplar ejemplarBusqueda;
		Confirmacion confirmacion = new Confirmacion();

		prestamoBusqueda = (Prestamo) repo.find(Prestamo.class,prestamo.getId());

		ejemplarBusqueda = (Ejemplar) repo.find(Ejemplar.class,prestamo.getEjemplar().getId());

		if (prestamoBusqueda == null) {
			confirmacion.setEstado(Constants.CONFIRMACION_ERROR);
		} else {
			prestamoBusqueda.setEstadoPrestamo(EstadoPrestamo.finalizado);
			 prestamoBusqueda.setFechaDeDevolucion(prestamo.getFechaHasta());
			ejemplarBusqueda.setEstadoEjemplar(EstadoEjemplar.disponible);
			
			logger.info("savePrestamo");
			repo.save(prestamoBusqueda);
			
			confirmacion.setEstado(Constants.CONFIRMACION_OK);
		}
		
		if(estadoDevolucion==Constants.MAL_ESTADO){
			mailServices.libroMalEstado(prestamo.getUsuario());
		}
		return confirmacion;
	}
	
	@Override
	@Transactional
	public Confirmacion recomendarLibro(Usuario usuarioRecibe,Usuario usuarioEnvia, Libro libro){
		Confirmacion confirmacion = new Confirmacion();	
		
		mailServices.recomendarLibro(libro.getId(),usuarioRecibe,usuarioEnvia);
		confirmacion.setEstado(Constants.CONFIRMACION_OK);
		return confirmacion;
	}
	
	@Override
	@Transactional
	public Confirmacion bloquearUsuario(Usuario usuario, Boolean estadoAlta){
		Confirmacion confirmacion = new Confirmacion();
		Usuario usuarioBuscado;
		
		usuarioBuscado = (Usuario) repo.find(Usuario.class, "nombreUsuario", usuario.getNombreUsuario());
		usuarioBuscado.setEstadoAlta(estadoAlta);

		logger.info("saveUsuario");
		repo.save(usuarioBuscado);
		
		confirmacion.setEstado(Constants.CONFIRMACION_OK);			
		return confirmacion;
	}
	
	@Override
	@Transactional
	public Confirmacion cambiarResponsabilidad(Usuario usuario, String responsabilidadNueva){
		Confirmacion confirmacion = new Confirmacion();
		Usuario usuarioBuscado;
		
		usuarioBuscado = (Usuario) repo.find(Usuario.class, "nombreUsuario", usuario.getNombreUsuario());
		usuarioBuscado.setResponsabilidad(responsabilidadNueva);

		logger.info("saveUsuario");
		repo.save(usuarioBuscado);
		
		confirmacion.setEstado(Constants.CONFIRMACION_OK);		
		return confirmacion;
	}
		
	@Override
	@Transactional
	public Confirmacion cambiarNovedadesUsuario(Usuario usuario,List <Categoria> novedades){
		Confirmacion confirmacion = new Confirmacion();	
		Usuario usuarioBuscado;
		usuarioBuscado = (Usuario) repo.find(Usuario.class, "nombreUsuario", usuario.getNombreUsuario());
		usuarioBuscado.setNovedades(novedades);
		
		logger.info("saveUsuario");
		repo.save(usuarioBuscado);
		
		confirmacion.setEstado(Constants.CONFIRMACION_OK);
		return confirmacion;
	}
	
	@Override
	@Transactional
	public Confirmacion cambiarDatosUsuario(Usuario usuario){
		Confirmacion confirmacion = new Confirmacion();	
		Usuario usuarioBuscado;
		usuarioBuscado = (Usuario) repo.find(Usuario.class, "nombreUsuario", usuario.getNombreUsuario());				
		logger.info("saveUsuario");
		repo.save(usuarioBuscado);
		
		confirmacion.setEstado(Constants.CONFIRMACION_OK);
		return confirmacion;
	}
	
	
	@Override
	@Transactional
	public Confirmacion extenderPrestamo(Prestamo prestamo,Date diaHasta){		
		Confirmacion confirmacion = new Confirmacion();
		Prestamo prestamoBusqueda = (Prestamo) repo.find(Prestamo.class,prestamo.getId());
		
		prestamoBusqueda.setFechaHasta(diaHasta);
		
		logger.info("savePrestamo");
		repo.save(prestamoBusqueda);
		
		confirmacion.setEstado(Constants.CONFIRMACION_OK);
		return confirmacion;
	}
	
	@Override
	@Transactional	
	public Confirmacion reasignarPrestamo(Prestamo prestamo,Usuario usuarioNuevo){
		Confirmacion confirmacion = new Confirmacion();
		Prestamo prestamoBusqueda;

		prestamoBusqueda = (Prestamo) repo.find(Prestamo.class,prestamo.getId());
		
		prestamoBusqueda.setUsuario(usuarioNuevo);
		
		logger.info("savePrestamo");
		repo.save(prestamoBusqueda);
		
		confirmacion.setEstado(Constants.CONFIRMACION_OK);
		return confirmacion;
	}
		
	@Override
	@Transactional
	public Confirmacion cambiarSugerencia(Sugerencia sugerencia){
		Confirmacion confirmacion = new Confirmacion();
				
		logger.info("saveSugerencia");
		repo.save(sugerencia);		

		confirmacion.setEstado(Constants.CONFIRMACION_OK);
		
		return confirmacion;
	}

	public Confirmacion cambiarSugerenciaPDFsubido(SugerenciaPDFsubido sugerenciaPDFsubido){
		Confirmacion confirmacion = new Confirmacion();		
		
		logger.info("saveSugerencia");
		repo.save(sugerenciaPDFsubido);
		
		confirmacion.setEstado(Constants.CONFIRMACION_OK);

		return confirmacion;
	}
	
	@Descachea(cacheKey="categorias") 
	public Confirmacion editarCategoria(Categoria categoria){
		Confirmacion confirmacion = new Confirmacion();
		
		logger.info("saveCategoria");
		repo.save(categoria);
		
		confirmacion.setEstado(Constants.CONFIRMACION_OK);
		return confirmacion;
	}
	
	@Descachea(cacheKey="oficinas") 
	public Confirmacion editarOficina(Oficina oficina){
		Confirmacion confirmacion = new Confirmacion();
		Domicilio domicilioBusqueda;
		
		domicilioBusqueda = (Domicilio) repo.find(Domicilio.class,oficina.getDomicilio().getId());
			
		domicilioBusqueda.setCodigoPostal(oficina.getDomicilio().getCodigoPostal());
		domicilioBusqueda.setDireccion(oficina.getDomicilio().getDireccion());
		domicilioBusqueda.setLocalidad(oficina.getDomicilio().getLocalidad());
		domicilioBusqueda.setPais(oficina.getDomicilio().getPais());
		domicilioBusqueda.setProvincia(oficina.getDomicilio().getProvincia());
		
		logger.info("saveOficina");
		repo.save(oficina);
		
		logger.info("saveDomicilio");
		repo.save(domicilioBusqueda);
		
		confirmacion.setEstado(Constants.CONFIRMACION_OK);
		return confirmacion;	
	}
	
	public Oficina buscarOficina(String string) {
		Oficina oficinaBuscada;

		oficinaBuscada = (Oficina) repo.find(Oficina.class, "nombre", string);

		return oficinaBuscada;
	}

	@Transactional
	@Override
	@Descachea(cacheKey = "libros")
	public Confirmacion prestarEjemplar(Prestamo prestamo) {
		Confirmacion confirmacion = new Confirmacion();
		confirmacion.setEstado(Constants.CONFIRMACION_OK);
		prestamo.setEstadoPrestamo(EstadoPrestamo.enVigencia);
		
		if(prestamo.getEjemplar().getEstadoEjemplar().toString().equalsIgnoreCase(EstadoEjemplar.disponible.toString())){
			Usuario usuarioBuscado=(Usuario)repo.find(Usuario.class, prestamo.getUsuario().getId());
			if(usuarioBuscado.getEstadoAlta()){
				prestamo.getEjemplar().setEstadoEjemplar(EstadoEjemplar.prestado);
				prestamo = (Prestamo) repo.save(prestamo);
				confirmacion.setEstado(Constants.CONFIRMACION_OK);
			}else{
				// TODO: gabrielflores: 2011 05 11: devolver mensaje de error de usuario no activo...
				confirmacion.setEstado(Constants.CONFIRMACION_ERROR);
				confirmacion.setErrorString("usuario no activo");
			}
		}else{
			// TODO: gabrielflores: 2011 05 11: devolver mensaje de error de ejemplar no disponible...
			confirmacion.setEstado(Constants.CONFIRMACION_ERROR);
			confirmacion.setErrorString("ejemplar no disponible");
		}
		return confirmacion;
	}

	@Override
	public Prestamo buscarPrestamo(Ejemplar ejemplar) {
		Prestamo prestamoBuscado=null;	
		List <Prestamo> lista = listarService.listarPrestamos();
		
		for(Prestamo prestamo:lista){
			if(prestamo.getEjemplar().getId().compareTo(ejemplar.getId())== 0){
				prestamoBuscado=prestamo;
				break;
			}
		}
		return prestamoBuscado;
	}
	
	@Override
	@Descachea(cacheKey = "libros")
	public Confirmacion eliminarEjemplar(Ejemplar ejemplar){
		Confirmacion confirmacion = new Confirmacion();
		confirmacion.setEstado(Constants.CONFIRMACION_OK);
		Calendar cal = Calendar.getInstance();
		Date date = cal.getTime();
		ejemplar.setFechaBaja(date);

		LibroFisico libroFisico = new LibroFisico();
		libroFisico = ejemplar.getLibrofisico();
		repo.save(ejemplar);
		
		int ejemplaresBaja = 0;
		
		for ( Ejemplar searchEjemplar : libroFisico.getEjemplar() ){
			if (searchEjemplar.getFechaBaja() != null)
				ejemplaresBaja++ ;
		}
		if(ejemplaresBaja == libroFisico.getEjemplar().size()){
			libroFisico.setFechaBaja(date);
			libroFisico.setImagen(null);
			repo.save(libroFisico);
			
			if( libroFisico.getImagen() != null )
			{			
				String imagenesPath = FlexContext.getServletContext().getRealPath("imagenes");
				if ( libroFisico.getImagen() != null && !libroFisico.getImagen().isEmpty() )
				{
					File fileImagenLibro = new File(imagenesPath, libroFisico.getImagen());
					fileImagenLibro.delete();
				}
			}
			
		}
		defaultMessageTemplate.send("actualizacion", Constants.ACTUALIZACION_LIBROS );
		
		return confirmacion;
	}
	
	public Usuario buscarUser(Usuario usuario){
		Usuario usuarioBuscado=null;
		usuarioBuscado = (Usuario) repo.find(Usuario.class, "nombreUsuario", usuario.getNombreUsuario());
		return usuarioBuscado;
	}
	
	@Override
	public Confirmacion eliminarPDFsubido(SugerenciaPDFsubido pdfSubido,String mensaje){
		Confirmacion confirmacion = new Confirmacion();
		SugerenciaPDFsubido sugerenciaBuscada;	 
		sugerenciaBuscada=(SugerenciaPDFsubido)repo.find(SugerenciaPDFsubido.class, pdfSubido.getId());		 
		sugerenciaBuscada.setEstadoSugerenciaPDFsubido(EstadoSugerenciaPDFsubido.rechazada);
		Usuario usuario = pdfSubido.getUsuario();


		if( sugerenciaBuscada.getImagen() != null )
		{			
			String imagenesPath = FlexContext.getServletContext().getRealPath("imagenes");
			if ( sugerenciaBuscada.getImagen() != null && !sugerenciaBuscada.getImagen().isEmpty() )
			{
				File fileImagenLibro = new File(imagenesPath, sugerenciaBuscada.getImagen());
				fileImagenLibro.delete();
			}
		}
		
		if( !sugerenciaBuscada.getUrl().isEmpty() )
		{			
			String pdfsPath = FlexContext.getServletContext().getRealPath("pdfs");
			if ( sugerenciaBuscada.getUrl() != null && !sugerenciaBuscada.getUrl().isEmpty() )
			{
				File filePdfLibro = new File(pdfsPath, sugerenciaBuscada.getUrl());
				filePdfLibro.delete();
			}
		}
		sugerenciaBuscada.setUrl(null);
		sugerenciaBuscada.setImagen(null);
		repo.save(sugerenciaBuscada);
		mailServices.rechazarPdf(usuario,mensaje);
		confirmacion.setEstado(Constants.CONFIRMACION_OK);
		defaultMessageTemplate.send("actualizacion", Constants.ACTUALIZACION_SUGERENCIAS_PDF );
		return confirmacion;
	}

	@Override
	@Descachea(cacheKey = "libros")
	public Confirmacion eliminarLibro(Libro libro){
		Confirmacion confirmacion = new Confirmacion();

		if (libro instanceof LibroFisico) {
			logger.info("eliminaLibroFisico");
			int ejemplaresBaja = 0;
			//verifica que todos sus ejemplares esten disponibles o dados de baja
			LibroFisico libroFisico = (LibroFisico) libro;
			for ( Ejemplar searchEjemplar : libroFisico.getEjemplar() ){
				if (searchEjemplar.getFechaBaja() != null || searchEjemplar.getEstadoEjemplar() == EstadoEjemplar.disponible)
					ejemplaresBaja++ ;
			}		
			if(ejemplaresBaja == libroFisico.getEjemplar().size()){
				
				if( libroFisico.getImagen() != null )
				{			
					String imagenesPath = FlexContext.getServletContext().getRealPath("imagenes");
					if ( libro.getImagen() != null && !libro.getImagen().isEmpty() )
					{
						File fileImagenLibro = new File(imagenesPath, libro.getImagen());
						fileImagenLibro.delete();
					}
				}
				libroFisico.setFechaBaja(new Date());
				libroFisico.setImagen(null);
				repo.save(libroFisico);
				
				for ( Ejemplar searchEjemplar1 : libroFisico.getEjemplar() ){
					searchEjemplar1.setFechaBaja(new Date());
					repo.save(searchEjemplar1);
				}		
				defaultMessageTemplate.send("actualizacion", Constants.ACTUALIZACION_LIBROS );
				
			}
			confirmacion.setEstado(Constants.CONFIRMACION_OK);	
		} else if (libro instanceof LibroDigital) {
			logger.info("eliminaLibroDigital");

			LibroDigital libroDigital = (LibroDigital) libro;		
		
			if( libroDigital.getUrl() != null )
			{			
				String pdfsPath = FlexContext.getServletContext().getRealPath("pdfs");
				if ( libroDigital.getUrl() != null && !libroDigital.getUrl().isEmpty() )
				{
					File filePdfLibro = new File(pdfsPath, libroDigital.getUrl());
					filePdfLibro.delete();
				}
			}
			
			if( libro.getImagen() != null )
			{			
				String imagenesPath = FlexContext.getServletContext().getRealPath("imagenes");
				if ( libro.getImagen() != null && !libro.getImagen().isEmpty() )
				{
					File fileImagenLibro = new File(imagenesPath, libro.getImagen());
					fileImagenLibro.delete();
				}
			}
			libroDigital.setFechaBaja(new Date());
			libroDigital.setUrl(null);
			repo.save(libroDigital);
			defaultMessageTemplate.send("actualizacion", Constants.ACTUALIZACION_LIBROS );
			confirmacion.setEstado(Constants.CONFIRMACION_OK);
		}	
		return confirmacion;
	}
	
	@Transactional	
	@SuppressWarnings("unchecked")
	public Usuario buscarUsuario(String nombreUsuario){
		Usuario usuarioBuscado=null;
		usuarioBuscado = (Usuario) repo.find(Usuario.class, "nombreUsuario",nombreUsuario);
		
		if(usuarioBuscado==null)
		{
			AndFilter andFilter = new AndFilter();
		    andFilter.and(new EqualsFilter("objectclass","person"));
		    andFilter.and(new EqualsFilter("sAMAccountName",nombreUsuario));
		    
			List<Usuario> list = ldapTemplate.search("OU=Allusers", 
					andFilter.encode(), new UsuarioAttributesMapper());
			usuarioBuscado =(Usuario)list.get(0);
			usuarioBuscado = (Usuario) repo.save(usuarioBuscado);

		}
		return usuarioBuscado;
	}
	
	
	
	private class UsuarioAttributesMapper implements AttributesMapper {
	      @SuppressWarnings("unchecked")
		public Object mapFromAttributes(Attributes attrs) throws NamingException {
	    	Usuario usuario = new Usuario();	
	    	
	    	String nombre = attrs.get("displayName").toString();
			nombre = nombre.substring(nombre.indexOf(':')+2);
			
			String mail = attrs.get("mail").toString();
			mail = mail.substring(mail.indexOf(':')+2);
			
			String nombreUsuario = mail.substring(0, mail.indexOf('@')).toLowerCase();
			
			String office  = attrs.get("physicalDeliveryOfficeName").toString().toLowerCase();
			office = office.substring(office.indexOf(':')+2);
			char first=Character.toUpperCase(office.charAt(0));
			office=first+office.substring(1);
	    	
	    	Oficina oficina=(Oficina) repo.find(Oficina.class, "nombre",office);
	    	
	    	usuario.setNombreUsuario(nombreUsuario);	 
	    	usuario.setResponsabilidad(Constants.RESPONSABILIDAD_USUARIO);
	    	usuario.setEstadoAlta(true);
	    	usuario.setIdioma("castellano");
			usuario.setOficina(oficina);
			usuario.setCorreoElectronico(mail);
			
	        return usuario;
	      }
	   }
	
	public LibroFisico buscarLibro(LibroFisico libro){
		LibroFisico libroBuscado=null;
		
		libroBuscado = (LibroFisico) repo.find(LibroFisico.class, "nombre", libro.getNombre());
		 return libroBuscado;
	}

	public byte[] buscarImagenLibro( Libro libro )
	{
		byte[] imagen = null;
		
		if( !libro.getImagen().isEmpty() )
		{			
			String imagenesPath = FlexContext.getServletContext().getRealPath("imagenes");
			File fileImagenLibro = new File(imagenesPath, libro.getImagen());
			
			try {
				FileInputStream fileInputStream = new FileInputStream(fileImagenLibro);
				imagen = new byte[fileInputStream.available()]; 
				
				while( fileInputStream.available() > 0 )
				{
					fileInputStream.read(imagen, 0, imagen.length );
					
				}
				fileInputStream.close();
			} catch (FileNotFoundException e) {
				e.printStackTrace();
				//todo retornar false...
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
		}
		return imagen;
	}
	
	public List<Comentario> buscarComentariosLibro(Libro libro){
		return (List<Comentario>) repo.getListCondicional(Comentario.class,"comentariolibro",libro);
	}

}
